package com.googlecode.restitory.gae.xml;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class XMLParser {

	private final List<Node> nodes = new ArrayList<Node>();

	public XMLParser find(String query) {
		try {
			XPathFactory factory = XPathFactory.newInstance();
			XPath xpath = factory.newXPath();
			XPathExpression expr = xpath.compile(query);
			XMLParser ret = new XMLParser();
			for (Node node : this.nodes) {
				NodeList result = (NodeList) expr.evaluate(node, XPathConstants.NODESET);
				convertNodeList(result, ret.nodes);
			}
			return ret;
		} catch (XPathExpressionException e) {
			throw new RuntimeException(e);
		}
	}

	private void convertNodeList(NodeList result, List<Node> nodes) {
		for (int i = 0; i < result.getLength(); i++) {
			Node node = result.item(i);
			nodes.add(node);
		}
	}

	public int getLength() {
		return size();
	}

	public XMLParser item(int i) {
		return get(i);
	}

	public int size() {
		return nodes.size();
	}

	public XMLParser get(int i) {
		XMLParser ret = new XMLParser();
		ret.nodes.add(nodes.get(i));
		return ret;
	}

	public String getContent() {
		if (nodes.isEmpty()) {
			return null;
		}
		Node node = nodes.get(0);
		String ret = node.getNodeValue();
		return (ret == null ? null : ret.trim());
	}

	public String getContent(String parent) {
		if (nodes.isEmpty()) {
			return null;
		}

		for (Node node : nodes) {
			if (node.getParentNode().getNodeName().equals(parent)) {
				return node.getNodeValue();
			}
		}
		return null;
	}

	public static XMLParser create(List<Node> nodes) {
		XMLParser ret = new XMLParser();
		ret.nodes.addAll(nodes);
		return ret;
	}

	public static XMLParser parse(byte[] xml) {
		try {
			XMLParser ret = new XMLParser();
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setNamespaceAware(true);
			factory.setCoalescing(true);
			DocumentBuilder builder = factory.newDocumentBuilder();
			Document root = builder.parse(new ByteArrayInputStream(xml));
			ret.nodes.add(root);
			return ret;
		} catch (FactoryConfigurationError e) {
			throw new RuntimeException(e);
		} catch (ParserConfigurationException e) {
			throw new RuntimeException(e);
		} catch (SAXException e) {
			throw new RuntimeException(e);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public String toString() {
		return "" + nodes;
	}

	public void sort(Comparator<XMLParser> comparator) {
		List<XMLParser> list = new ArrayList<XMLParser>(nodes.size());
		for (Node node : nodes) {
			list.add(create(node));
		}
		Collections.sort(list, comparator);
		nodes.clear();
		for (XMLParser parser : list) {
			nodes.addAll(parser.nodes);
		}
	}

	public static XMLParser create(Node... list) {
		return create(Arrays.asList(list));
	}

}
